<?php


namespace BeReborn\Cache;


use BeReborn\Base\Component;
use Exception;
use Swoole\Coroutine\System;

/**
 * Class Export
 * @package BeReborn\Cache
 */
class Export extends Component
{

	/** @var \Redis */
	public $redis;

	public $index = 0;

	public $savePath = '';

	/**
	 * @param string $dir
	 * @throws Exception
	 */
	public function saveTo($dir = '')
	{
		$this->savePath = $this->createSavePath($dir);
		$this->export();
	}

	/**
	 * @param string $savePath
	 * @return string
	 * @throws Exception
	 */
	private function createSavePath($savePath = '')
	{
		if (empty($savePath)) {
			$savePath = \BeReborn::getRuntimePath();
		}

		if (!is_dir($savePath)) {
			throw new Exception('Sorry, Save path is not a directory.');
		}

		$savePath .= '/redis_export_' . $this->index . '_' . time() . '.data';
		if (!is_writable($savePath)) {
			throw new Exception('Sorry, No File Operating Permission.');
		}
		return $savePath;
	}

	/**
	 * @throws Exception
	 */
	private function export()
	{
		$this->redis->select($this->index);

		$iterator = '0';

		$total = $this->redis->dbSize();
		$progress = 0;

		$this->debug('waite export num ' . $total);
		while ($data = $this->redis->scan($iterator, null, 5000)) {
			if (empty($data)) {
				break;
			}
			$_data = [];
			foreach ($data as $key => $val) {
				$_data[] = $this->typeExport($val);
			}
			if (!empty($_data)) {
				$this->write($_data, $this->savePath);
			}
			$progress += count($_data);
			$this->debug('export progress ' . ($progress / $total) . '%');
		}
	}

	/**
	 * @param $data
	 * @param null $path
	 * @throws Exception
	 */
	private function write($data, $path = null)
	{
		$data = json_encode($data, JSON_UNESCAPED_UNICODE) . PHP_EOL;
		if (empty($path)) {
			$path = \BeReborn::getRuntimePath('') . '/redis.json';
		}
		System::writeFile($path, $data, FILE_APPEND);
	}

	/**
	 * @param $val
	 * @return array
	 */
	private function typeExport($val)
	{
		$_tmp = [];
		$_tmp['value'] = $val;
		$_tmp['type'] = $this->redis->type($val);
		if ($_tmp['type'] == \Redis::REDIS_SET) {
			$_tmp['data'] = $this->redis->sMembers($val);
		} else if ($_tmp['type'] == \Redis::REDIS_ZSET) {
			$_tmp['data'] = $this->getDataByZSet($val);
		} else if ($_tmp['type'] == \Redis::REDIS_HASH) {
			$_tmp['data'] = $this->redis->hGetAll($val);
		} else if ($_tmp['type'] == \Redis::REDIS_STRING) {
			$_tmp['data'] = $this->redis->get($val);
		} else if ($_tmp['type'] == \Redis::REDIS_LIST) {
			$_tmp['data'] = $this->getDataByLRange($val);
		} else {
			$_tmp['data'] = $this->redis->get($val);
		}
		return $_tmp;
	}

	/**
	 * @param $val
	 * @return array
	 */
	private function getDataByZSet($val)
	{
		$_tmp = [];
		$count = $this->redis->sCard($val);
		$lists = $this->redis->zRange($val, 0, $count);
		foreach ($lists as $newval) {
			$_tmp[] = [$newval, $this->redis->zScore($val, $newval)];
		}
		return $_tmp;
	}

	/**
	 * @param $val
	 * @return array
	 */
	private function getDataByLRange($val)
	{
		return $this->redis->lRange($val, 0, $this->redis->lLen($val));
	}

}
